package ai.ls.springmvc.servlet;

import ai.ls.springmvc.annotation.Autowired;
import ai.ls.springmvc.annotation.Controller;
import ai.ls.springmvc.annotation.RequestMapping;
import ai.ls.springmvc.annotation.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.springmvc.servlet
 * @Author: ls
 * @CreateTime: 2020-12-29 16:56
 * @Description: 模拟DispatcherServlet
 */
public class DispatcherServlet extends HttpServlet {

    List<String> clazzNames = new ArrayList<>();

    Map<String, Object> beans = new HashMap<>();

    Map<String, Object> handlerMethod = new HashMap<>();

    public DispatcherServlet() {
    }

    @Override
    public void init() throws ServletException {
        //1、包扫描
        scanPackage("ai.ls");
        //2、实例化类
        classInstance();
        //3.依赖注入/自动装配
        inject();
        //4、url和Controller中method的关系映射
        handlerMapping();


    }
    private void  handlerMapping() {
        if (beans.isEmpty()) {
            System.out.println("没有实例化的类");
            return;
        }
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //从集合中获取类实例
            Object instance = entry.getValue();
            if (instance.getClass().isAnnotationPresent(Controller.class)) {
                RequestMapping requestMapping = instance.getClass().getAnnotation(RequestMapping.class);
                //获取RequestMapping中定义的路径值
                String path = requestMapping.value();
                Method[] methods = instance.getClass().getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
                        String value = methodMapping.value();
                        //类上的路径+方法上路径
                        handlerMethod.put(path + value, method);
                    }
                }

            }

        }
    }
    private void inject() {
        if (beans.isEmpty()) {
            System.out.println("没有实例化的类");
            return;
        }
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //从集合中获取类实例
            Object instance = entry.getValue();
            //获取类中的所有成员属性
            Field[] fields = instance.getClass().getDeclaredFields();
            for (Field field : fields) {
                //判断成员属性上有无Autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String value = autowired.value();

                    field.setAccessible(true);

                    try {
                        //完成依赖注入
                        field.set(instance,beans.get(value));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }

            }
        }

    }
    private void classInstance() {
        if (clazzNames.isEmpty()) {
            System.out.println("没有扫描到类");
            return;
        }
        for (String name : clazzNames) {
            String realName = name.replace(".class", "");
            try {
                Class<?> aClass = Class.forName(realName);

                if (aClass.isAnnotationPresent(Controller.class)) {
                    Controller controller = aClass.getAnnotation(Controller.class);
                    //完成Controller的实例化
                    Object instance = aClass.newInstance();

                    RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);
                    String mappingValue = requestMapping.value();

                    //类的映射路径和类的实例化对象进行绑定
                    beans.put(mappingValue, instance);
                }

                if (aClass.isAnnotationPresent(Service.class)) {
                    Service service = aClass.getAnnotation(Service.class);
                    //完成Service的实例化
                    Object instance = aClass.newInstance();

                    String mappingValue = service.value();
                    //类的映射路径和类的实例化对象进行绑定
                    beans.put(mappingValue, instance);
                }
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private void scanPackage(String basePackage) {
        String path = basePackage.replaceAll("\\.", "/");
        //相对路径fileName前不加/
        URL url = getClass().getClassLoader().getResource(path);
        String filePath = url.getFile();
        //目录递归扫描
        File[] files = new File(filePath).listFiles();
        for (File file : files) {
            //判断是不是目录
            if (file.isDirectory()) {
                scanPackage(basePackage + "." + file.getName());
            } else {
                clazzNames.add(basePackage + "." + file.getName());
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doGet(req, resp);
        resp.getWriter().append("served at: ").append(req.getContextPath());
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = uri.replace(contextPath, "");
        Method method = (Method)handlerMethod.get(path);
        Object instance = beans.get("/" + path.split("/")[1]);
        try {
            Object object = method.invoke(instance, null);
            System.out.println(object.toString());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
